﻿using System.Diagnostics.Contracts;
using System.Reactive.Concurrency;

namespace System.Reactive
{
	[ContractClass(typeof(NotificationContract<>))]
	public abstract class Notification<T>
	{
		public abstract NotificationKind Kind
		{
			get;
		}

		public abstract bool HasValue
		{
			get;
		}

		public abstract T Value
		{
			get;
		}

		public abstract Exception Exception
		{
			get;
		}

		public abstract void Accept(IObserver<T> observer);

		public abstract TResult Accept<TResult>(IObserver<T, TResult> observer);

		public abstract void Accept(Action<T> onNext, Action<Exception> onError, Action onCompleted);

		public abstract TResult Accept<TResult>(Func<T, TResult> onNext, Func<Exception, TResult> onError, Func<TResult> onCompleted);

		public IObservable<T> ToObservable()
		{
			Contract.Ensures(Contract.Result<IObservable<T>>() != null, null, "Contract.Result<IObservable<T>>() != null");
			return null;
		}

		public IObservable<T> ToObservable(IScheduler scheduler)
		{
			Contract.Requires(scheduler != null, null, "scheduler != null");
			Contract.Ensures(Contract.Result<IObservable<T>>() != null, null, "Contract.Result<IObservable<T>>() != null");
			return null;
		}
	}

	[ContractClassFor(typeof(Notification<>))]
	internal abstract class NotificationContract<T> : Notification<T>
	{
		public override NotificationKind Kind
		{
			get
			{
				return default(NotificationKind);
			}
		}

		public override bool HasValue
		{
			get
			{
				Contract.Ensures(Contract.Result<bool>() == (Kind == NotificationKind.OnNext), null, "Contract.Result<bool>() == (Kind == NotificationKind.OnNext)");
				return false;
			}
		}

		public override T Value
		{
			get
			{
				Contract.Requires(HasValue == true, null, "HasValue == true");
				return default(T);
			}
		}

		public override Exception Exception
		{
			get
			{
				Contract.Ensures(!object.ReferenceEquals(Contract.Result<Exception>(), null) == (Kind == NotificationKind.OnError), null, "!object.ReferenceEquals(Contract.Result<Exception>(), null) == (Kind == NotificationKind.OnError)");
				return null;
			}
		}

		public override void Accept(IObserver<T> observer)
		{
			Contract.Requires(observer != null, null, "observer != null");
		}

		public override TResult Accept<TResult>(IObserver<T, TResult> observer)
		{
			Contract.Requires(observer != null, null, "observer != null");
			return default(TResult);
		}

		public override void Accept(Action<T> onNext, Action<Exception> onError, Action onCompleted)
		{
			Contract.Requires(onNext != null, null, "onNext != null");
			Contract.Requires(onError != null, null, "onError != null");
			Contract.Requires(onCompleted != null, null, "onCompleted != null");
		}

		public override TResult Accept<TResult>(Func<T, TResult> onNext, Func<Exception, TResult> onError, Func<TResult> onCompleted)
		{
			Contract.Requires(onNext != null, null, "onNext != null");
			Contract.Requires(onError != null, null, "onError != null");
			Contract.Requires(onCompleted != null, null, "onCompleted != null");
			return default(TResult);
		}
	}
}